﻿using System;
using System.ServiceModel;
using System.Data;
using System.Runtime.Serialization;
using System.IO;
using System.Configuration;
using System.Configuration.Install;
using System.ComponentModel;
using System.ServiceProcess;
using System.Threading;
using System.Collections;
using System.Xml;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;
using System.Data.SqlClient;
using System.Linq;
using System.ServiceModel.Description;
using System.Net;

using NLog;

namespace PUZZLEgestServer
{
    class Program
    {
        #region Private Members
        
        private static Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Login Service
        /// </summary>
        private static ServiceHost hostLogin = null;

        /// <summary>
        /// hostUserAdministration Service
        /// </summary>
        private static ServiceHost hostUserAdministration = null;

        #endregion

        static void Main(string[] args)
        {
             
            //System.Collections.Generic.List<PUZZLEgestServer.SVC.ModuleTablesPermissions> l = new System.Collections.Generic.List<SVC.ModuleTablesPermissions>();
            //l.Add( new SVC.ModuleTablesPermissions() { Module = "UserAdministration", Table= "Users", Create   = true , Read = true, Update = true, Delete = true });
            //l.Add( new SVC.ModuleTablesPermissions() { Module = "UserAdministration", Table = "Users", Create = true, Read = true, Update = true, Delete = true });
            //System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Generic.List<PUZZLEgestServer.SVC.ModuleTablesPermissions>));
            //System.IO.StringWriter sw = new System.IO.StringWriter();
            //ser.Serialize(sw, l);

            //Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("PUZZLEgestServer.SVC.UserAdministration.ModuleTables.xml");
            //ser = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Generic.List<PUZZLEgestServer.SVC.ModuleTablesPermissions>));
            //object o = ser.Deserialize(stream);

            //PUZZLEgestServer.DAL.Entities ent = new DAL.Entities(DAL.DataAccess.SuperAdminConnectionStringEntity);
            
            //PUZZLEgestServer.DAL.User userEntity = new DAL.User()
            //    {
            //        name = "dafsa"
            //    };
            //Functions.EntityToDTO(userEntity);

            //byte[] b1 = Util.Encryption.TripleDES.Encrypt("caca;maca;pipi");
            //string test = System.Text.Encoding.GetEncoding(1252).GetString(b1);

            ////byte[] b2 = System.Text.Encoding.GetEncoding(1252).GetBytes(test);

            //string retTest = Util.Encryption.TripleDES.Decrypt(test);

            //DataAccess.SuperAdminConnectionString = "caca;maca;pipi";

            //string s = DataAccess.SuperAdminConnectionString;

            //SqlConnection conn = new SqlConnection(@"Data Source=PC0085\SQLEXPRESS;Initial Catalog=puzzlegestv2;User ID=alex;Password=alex;MultipleActiveResultSets=True");
            //conn.Open();

            ////DataTable t = new DataTable();
            ////using (SqlCommand comm = new SqlCommand("SELECT USER", conn))
            ////{
            ////    using (SqlDataAdapter da = new SqlDataAdapter(comm))
            ////    {
            ////        da.Fill(t);
            ////    }
            ////}

            //SqlTransaction tran = conn.BeginTransaction();
            //try
            //{

            //    using (SqlCommand comm = new SqlCommand("update [Users] set firstname = 'fhgdsfdsfdsfds' where id = 6", conn, tran))
            //    {
            //        comm.ExecuteNonQuery();
            //    }

            //    using (SqlCommand comm = new SqlCommand("delete from [Users] where id = 7", conn, tran))
            //    {
            //        comm.ExecuteNonQuery();
            //    }

            //    tran.Commit();
            //}
            //catch (Exception ex)
            //{
            //    tran.Rollback();
            //    throw;
            //}

            try 
	        {
                /*
                 * Check the args of the program for the override super admin credentials call
                 */
                if (args != null && args.Length > 0)
                {
                    if (args[0] == "-superadmin" || args[0] == "superadmin")
                    {
                        logger.Info("Changeing the super admin password ...");

                        //check if there is allready a super admin password present
                        string user = Util.Globals.ReadRegistryKey("user", true);
                        string password = Util.Globals.ReadRegistryKey("password", true);

                        if (!string.IsNullOrEmpty(user))
                        {
                            Console.WriteLine("current user: " + user);
                            Console.Write("current password: ");
                            string currentPassword = InputConsolePassword();
                            if (password == currentPassword)
                            {
                                Console.Write("new user: ");
                                user = Console.ReadLine();
                                Console.Write("new password: ");
                                password = InputConsolePassword();
                            }
                            else
                            {
                                logger.Fatal("Invalid super admin password. Changing super admin credentials aborted.");
                                Console.ReadLine();
                                return;
                            }
                        }
                        else
                        {
                            Console.WriteLine("No super admin credentials found. Input new super admin credentials:");
                            Console.Write("user: ");
                            user = Console.ReadLine();
                            Console.Write("password: ");
                            password = InputConsolePassword();
                        }

                        //save the super admin credentials
                        Util.Globals.SaveRegistryKey("user", user, true);
                        Util.Globals.SaveRegistryKey("password", password, true);
                    }
                }


                logger.Info("Checking super admin connection with the db server ...");

                SqlConnection conn = null;
                
                try
                {
                    conn = new System.Data.SqlClient.SqlConnection(DAL.DataAccess.SuperAdminConnectionString);
                }
                catch (Exception ex)
                {
                    logger.Error("Super admin user or password are invalid. Press Y to enter credentials or N to exit");
                    if (Console.ReadLine().ToUpper() == "Y")
                    {
                        Console.Write("user: ");
                        string user = Console.ReadLine();
                        Console.Write("password: ");
                        string password = InputConsolePassword();

                        //save the credentials in the registry
                        Util.Globals.SaveRegistryKey("user", user, true);
                        Util.Globals.SaveRegistryKey("password", password, true);

                        return;
                    }
                    else
                        throw ex;
                }

                
                conn.Open();
                logger.Info("OK");
                Console.WriteLine();
                conn.Close();
            }
	        catch (Exception ex)
	        {
                logger.Info("FAILED");
                logger.FatalException("Server can not start", ex);
                Console.ReadLine();
                return;
	        }
           

            if (!StartAsService())
            {
                StartAsConsole();
            }
            else
            {
                logger.Error("JobServer is allready running as a windows service under the name of '{0}'", ConfigurationManager.AppSettings.Get("ServiceName"));
                Console.ReadLine();
            }
        }

        private static string InputConsolePassword()
        {
            string password = string.Empty;
            ConsoleKeyInfo info = Console.ReadKey(true);
            while (info.Key != ConsoleKey.Enter)
            {
                if (info.Key != ConsoleKey.Backspace)
                {
                    Console.Write("*");
                    password += info.KeyChar;
                }
                else if (info.Key == ConsoleKey.Backspace)
                {
                    if (!string.IsNullOrEmpty(password))
                    {
                        // remove one character from the list of password characters
                        password = password.Substring(0, password.Length - 1);
                        // get the location of the cursor
                        int pos = Console.CursorLeft;
                        // move the cursor to the left by one character
                        Console.SetCursorPosition(pos - 1, Console.CursorTop);
                        // replace it with space
                        Console.Write(" ");
                        // move the cursor to the left by one character again
                        Console.SetCursorPosition(pos - 1, Console.CursorTop);
                    }
                }
                info = Console.ReadKey(true);
            }
            Console.WriteLine();
            return password;
        }

        /// <summary>
        /// Start the job server as windows service
        /// </summary>
        /// <returns></returns>
        private static bool StartAsService()
        {
            try
            {
                ServiceController c = new ServiceController(ConfigurationManager.AppSettings.Get("ServiceName"));

                //test if the service is installed
                try
                {
                    if (c.Status == ServiceControllerStatus.Stopped)
                    {
                        bool dummy = true;
                    }
                }
                catch (Exception ex)
                {
                    return false;
                }

                if (c.Status == ServiceControllerStatus.StartPending)
                {
                    logger.Info(ConfigurationManager.AppSettings.Get("ServiceName") + " starting ...");
                    ServiceBase.Run(new PUZZLEgestWindowsService());
                    return true;
                }
                else if (c.Status == ServiceControllerStatus.Running)
                    return true;
                else
                    return false;
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null && ((System.ComponentModel.Win32Exception)(ex.InnerException)).NativeErrorCode != 1060)
                {
                    logger.ErrorException("", ex);
                }
                else
                {
                    logger.ErrorException("", ex);
                }

                return false;
            }

        }

        /// <summary>
        /// Start the jobserver as console application (from admin or stand alone)
        /// </summary>
        /// <returns></returns>
        private static bool StartAsConsole()
        {
            //start service
            StartServices();

            Console.ReadLine();

            CleanClose();

            return true;
        }

        /// <summary>
        /// Hosts the service in respect to the configuration file.
        /// </summary>
        /// <returns></returns>
        public static bool StartServices()
        {
            //Login service
            hostLogin = new ServiceHost(typeof(PUZZLEgestServer.SVC.Login));
            if (!StartService(hostLogin))
                return false;

            //User administration service
            hostUserAdministration = new ServiceHost(typeof(PUZZLEgestServer.SVC.UserAdministration));
            if (!StartService(hostUserAdministration))
                return false;

            return true;
        }

        private static bool StartService(ServiceHost host)
        {
            try
            {
                logger.Info("************* Service info *************");
                logger.Info("Name: {0}", host.Description.Name);
                logger.Info("End points:");

                ServiceEndpoint mex = null;
                foreach (ServiceEndpoint ep in host.Description.Endpoints)
                {
                    if (ep.Address.Uri.AbsoluteUri.Contains("localhost"))
                    {
                        ep.Address = new EndpointAddress(ep.Address.Uri.AbsoluteUri.Replace("localhost", Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString()));
                    }

                    if (ep.Address.Uri.AbsoluteUri.EndsWith("/mex"))
                    {
                        mex = ep;
                        continue;
                    }

                    logger.Info("\tEndpoint = {0}", ep.Address.Uri.AbsoluteUri);
                }

                Console.WriteLine();

                if (mex == null)
                {
                    logger.Warn("\tWARNING: no mex endpoint found!");
                }
                else
                {
                    logger.Info("\tMex = {0}", mex.Address.Uri.AbsoluteUri);
                }

                logger.Info("BaseAddress = {0}", host.BaseAddresses[0].AbsoluteUri);
                Console.WriteLine();
                logger.Info("Starting service ...");

                host.Open();

                logger.Info("OK");
                logger.Info("****************************************");

                return true;

            }
            catch (Exception ex)
            {
                logger.Info("Failed");
                logger.FatalException(ex.Message, ex);
                Console.WriteLine();
                return false;
            }
        }

        /// <summary>
        /// Closes all the objects and connection for a clean serive close.
        /// </summary>
        static private void CleanClose()
        {

        }

    }
}
